package logger

import (
	"context"
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/davecgh/go-spew/spew"
	"github.com/getsentry/raven-go"
	"github.com/gin-gonic/gin"

	"mychat/internal/common/cmctx"
	"mychat/internal/common/utils"
)

type Logger interface {
	WithCustomTag(tag LoggerTag) Logger
	WithCustomStrTag(tagstr string) Logger
	DebugCtx(ctx *gin.Context, fmtStr string, args ...any)
	InfoCtx(ctx *gin.Context, fmtStr string, args ...any)
	WarningCtx(ctx *gin.Context, fmtStr string, args ...any)
	ErrorCtx(ctx *gin.Context, fmtStr string, args ...any)
	FatalCtx(ctx *gin.Context, fmtStr string, args ...any)
	LogEntry(logEntry map[string]any)
	Debug(ctx context.Context, fmtStr string, args ...any)
	Info(ctx context.Context, fmtStr string, args ...any)
	Warning(ctx context.Context, fmtStr string, args ...any)
	Error(ctx context.Context, fmtStr string, args ...any)
	Fatal(ctx context.Context, fmtStr string, args ...any)
	GetLogger() Logger
}

type Level int32

const (
	DEBUG Level = iota
	INFO
	WARNING
	ERROR
	FATAL
)

func (l Level) String() string {
	switch l {
	case DEBUG:
		return "DEBUG"
	case INFO:
		return "INFO"
	case WARNING:
		return "WARNING"
	case ERROR:
		return "ERROR"
	case FATAL:
		return "FATAL"
	}
	return ""
}

func convertFromString(levelStr string) Level {
	switch strings.ToUpper(levelStr) {
	case DEBUG.String():
		return DEBUG
	case INFO.String():
		return INFO
	case WARNING.String(), "WARN":
		return WARNING
	case ERROR.String():
		return ERROR
	case FATAL.String():
		return FATAL
	}
	return INFO
}

func safeSprintf(fmtStr string, args ...any) (ret string) {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("spew.Sprintf panic||fmtStr=%v||args=%+v", fmtStr, args)
			ret = fmt.Sprintf(fmtStr, args...)
		}
	}()

	msg := spew.Sprintf(fmtStr, args...)
	return msg
}

func preprocessLogArgs(ctx any, fmtStr string, args []any) (string, []any) {
	var ctxStr string
	gtx, ok := ctx.(*gin.Context)
	if ok {
		if gtx == nil {
			return "", nil
		}
		ctxStr = cmctx.FormatGtxRequestId(gtx)
	} else {
		if ctx == nil {
			return "", nil
		}
		ctxStr = cmctx.FormatCtxRequestId(ctx.(context.Context))
	}

	fmtStr += "||_ctx=%s"
	args = append(args, ctxStr)
	return fmtStr, args
}

func captureErrorMessage(msg string) {
	filename, lineno, funcName := utils.GetCaller(2)
	tags := map[string]string{
		"gofile":   filename,
		"lineno":   strconv.Itoa(lineno),
		"funcName": funcName,
	}
	errStr, paramVals := parseLogMsg(msg)
	packet := raven.NewPacketWithExtra(errStr, paramVals)
	raven.Capture(packet, tags)
}

func parseLogMsg(msg string) (string, map[string]any) {
	paramVals := make(map[string]any)
	msgParts := strings.Split(msg, "||")
	errStr := msgParts[0]
	for i, p := range msgParts[1:] {
		flag := true
		var key, val string
		kvs := strings.SplitN(p, "=", 2)
		if len(kvs) == 0 {
			continue
		} else if len(kvs) == 1 {
			key = fmt.Sprintf("__part_%d", i)
			val = kvs[0]
		} else {
			key = kvs[0]
			val = kvs[1]
			if strings.Contains(val, "#") {
				vals := strings.SplitN(val, "#", 2)
				paramVals[key] = map[string]string{
					vals[0]: vals[1],
				}
				flag = false
			}
		}
		if flag {
			paramVals[key] = val
		}
	}
	return errStr, paramVals
}
